/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package bd.org.apache.commons.math.linear;

import bd.org.apache.commons.math.exception.DimensionMismatchException;
import bd.org.apache.commons.math.exception.MaxCountExceededException;
import bd.org.apache.commons.math.exception.NonSquareOperatorException;
import bd.org.apache.commons.math.exception.NullArgumentException;
import bd.org.apache.commons.math.util.IterationManager;
import bd.org.apache.commons.math.util.MathUtils;

/**
 * This abstract class defines an iterative solver for the linear system A
 * &middot; x = b. In what follows, the <em>residual</em> r is defined as r = b
 * - A &middot; x, where A is the linear operator of the linear system, b is the
 * right-hand side vector, and x the current estimate of the solution.
 *
 * @version $Id: IterativeLinearSolver.java 1244107 2012-02-14 16:17:55Z erans $
 * @since 3.0
 */
public abstract class IterativeLinearSolver {

    /**
     * The object in charge of managing the iterations.
     */
    private final IterationManager manager;

    /**
     * Creates a new instance of this class, with default iteration manager.
     *
     * @param maxIterations the maximum number of iterations
     */
    public IterativeLinearSolver(final int maxIterations) {

        this.manager = new IterationManager(maxIterations);
    }

    /**
     * Creates a new instance of this class, with custom iteration manager.
     *
     * @param manager the custom iteration manager
     * @throws NullArgumentException if {@code manager} is {@code null}
     */
    public IterativeLinearSolver(final IterationManager manager) throws NullArgumentException {

        MathUtils.checkNotNull(manager);

        this.manager = manager;
    }

    /**
     * Performs all dimension checks on the parameters of
     * {@link #solve(RealLinearOperator, RealVector, RealVector) solve} and
     * {@link #solveInPlace(RealLinearOperator, RealVector, RealVector) solveInPlace},
     * and throws an exception if one of the checks fails.
     *
     * @param a the linear operator A of the system
     * @param b the right-hand side vector
     * @param x0 the initial guess of the solution
     * @throws NullArgumentException if one of the parameters is {@code null}
     * @throws NonSquareOperatorException if {@code a} is not square
     * @throws DimensionMismatchException if {@code b} or {@code x0} have
     * dimensions inconsistent with {@code a}
     */
    protected static void checkParameters(final RealLinearOperator a, final RealVector b,
            final RealVector x0)
            throws NullArgumentException, NonSquareOperatorException, DimensionMismatchException {

        MathUtils.checkNotNull(a);
        MathUtils.checkNotNull(b);
        MathUtils.checkNotNull(x0);

        if (a.getRowDimension() != a.getColumnDimension()) {
            throw new NonSquareOperatorException(a.getRowDimension(), a.getColumnDimension());
        }

        if (b.getDimension() != a.getRowDimension()) {
            throw new DimensionMismatchException(b.getDimension(), a.getRowDimension());
        }

        if (x0.getDimension() != a.getColumnDimension()) {
            throw new DimensionMismatchException(x0.getDimension(), a.getColumnDimension());
        }
    }


    /**
     * Returns the iteration manager attached to this solver.
     *
     * @return the manager
     */
    public IterationManager getIterationManager() {

        return manager;
    }


    /**
     * Returns an estimate of the solution to the linear system A &middot; x =
     * b.
     *
     * @param a the linear operator A of the system
     * @param b the right-hand side vector
     * @return a new vector containing the solution
     * @throws NullArgumentException if one of the parameters is {@code null}
     * @throws NonSquareOperatorException if {@code a} is not square
     * @throws DimensionMismatchException if {@code b} has dimensions
     * inconsistent with {@code a}
     * @throws MaxCountExceededException at exhaustion of the iteration count,
     * unless a custom
     * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback}
     * has been set at construction
     */
    public RealVector solve(final RealLinearOperator a, final RealVector b)
            throws NullArgumentException, NonSquareOperatorException, DimensionMismatchException,
                   MaxCountExceededException {

        MathUtils.checkNotNull(a);

        final RealVector x = new ArrayRealVector(a.getColumnDimension());

        x.set(0.);

        return solveInPlace(a, b, x);
    }


    /**
     * Returns an estimate of the solution to the linear system A &middot; x =
     * b.
     *
     * @param a the linear operator A of the system
     * @param b the right-hand side vector
     * @param x0 the initial guess of the solution
     * @return a new vector containing the solution
     * @throws NullArgumentException if one of the parameters is {@code null}
     * @throws NonSquareOperatorException if {@code a} is not square
     * @throws DimensionMismatchException if {@code b} or {@code x0} have
     * dimensions inconsistent with {@code a}
     * @throws MaxCountExceededException at exhaustion of the iteration count,
     * unless a custom
     * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback}
     * has been set at construction
     */
    public RealVector solve(RealLinearOperator a, RealVector b, RealVector x0)
            throws NullArgumentException, NonSquareOperatorException, DimensionMismatchException,
                   MaxCountExceededException {

        MathUtils.checkNotNull(x0);

        return solveInPlace(a, b, x0.copy());
    }


    /**
     * Returns an estimate of the solution to the linear system A &middot; x =
     * b. The solution is computed in-place (initial guess is modified).
     *
     * @param a the linear operator A of the system
     * @param b the right-hand side vector
     * @param x0 initial guess of the solution
     * @return a reference to {@code x0} (shallow copy) updated with the
     *         solution
     * @throws NullArgumentException if one of the parameters is {@code null}
     * @throws NonSquareOperatorException if {@code a} is not square
     * @throws DimensionMismatchException if {@code b} or {@code x0} have
     * dimensions inconsistent with {@code a}
     * @throws MaxCountExceededException at exhaustion of the iteration count,
     * unless a custom
     * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback}
     * has been set at construction
     */
    public abstract RealVector solveInPlace(RealLinearOperator a, RealVector b, RealVector x0)
            throws NullArgumentException, NonSquareOperatorException, DimensionMismatchException,
                   MaxCountExceededException;
}
